<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  
  <meta name="description" content="Win10环境前后端分离项目基于Vue.js+Django+Python3实现微信(wechat)扫码支付流程(2021年最新攻略)
在生活中真正具有广泛性、高效性、使用方便性的支付方式还得是扫码支付，扫码的优点在于推广成本低，上至钓鱼台国宾馆，下至发廊地摊都能用，打印出来就完事了，而相比其他支付方" />
  

  
  
  
  
  
  
  <title>前后端分离实现微信支付 | 南辞的技术博客</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta name="description" content="Win10环境前后端分离项目基于Vue.js+Django+Python3实现微信(wechat)扫码支付流程(2021年最新攻略) 在生活中真正具有广泛性、高效性、使用方便性的支付方式还得是扫码支付，扫码的优点在于推广成本低，上至钓鱼台国宾馆，下至发廊地摊都能用，打印出来就完事了，而相比其他支付方式，现金的找零及假钞问题，信用卡的办理门槛、pos机的沉没成本，就算微信可集成的h5支付和小程序支付">
<meta property="og:type" content="article">
<meta property="og:title" content="前后端分离实现微信支付">
<meta property="og:url" content="https://yanlidoushikeke.gitee.io/myhexo/2021/04/23/%E5%89%8D%E5%90%8E%E7%AB%AF%E5%88%86%E7%A6%BB%E5%AE%9E%E7%8E%B0%E5%BE%AE%E4%BF%A1%E6%94%AF%E4%BB%98/index.html">
<meta property="og:site_name" content="南辞的技术博客">
<meta property="og:description" content="Win10环境前后端分离项目基于Vue.js+Django+Python3实现微信(wechat)扫码支付流程(2021年最新攻略) 在生活中真正具有广泛性、高效性、使用方便性的支付方式还得是扫码支付，扫码的优点在于推广成本低，上至钓鱼台国宾馆，下至发廊地摊都能用，打印出来就完事了，而相比其他支付方式，现金的找零及假钞问题，信用卡的办理门槛、pos机的沉没成本，就算微信可集成的h5支付和小程序支付">
<meta property="og:locale" content="en_US">
<meta property="og:image" content="https://v3u.cn/v3u/Public/Uploads/1608276523.png">
<meta property="og:image" content="https://v3u.cn/v3u/Public/js/editor/attached/20201218151220_48395.png">
<meta property="og:image" content="https://v3u.cn/v3u/Public/js/editor/attached/20201218151256_41580.png">
<meta property="og:image" content="https://v3u.cn/v3u/Public/js/editor/attached/20201218151258_71938.png">
<meta property="og:image" content="https://v3u.cn/v3u/Public/js/editor/attached/20201218151229_51971.png">
<meta property="og:image" content="https://v3u.cn/v3u/Public/js/editor/attached/20201218151248_41646.png">
<meta property="og:image" content="https://v3u.cn/v3u/Public/js/editor/attached/20201218161202_30556.png">
<meta property="og:image" content="https://v3u.cn/v3u/Public/js/editor/attached/20201218161240_52822.png">
<meta property="og:image" content="https://v3u.cn/v3u/Public/js/editor/attached/20201218161242_76881.png">
<meta property="og:image" content="https://v3u.cn/v3u/Public/js/editor/attached/20201218161259_75018.jpeg">
<meta property="og:image" content="https://v3u.cn/v3u/Public/js/editor/attached/20201218161259_98962.png">
<meta property="og:image" content="https://v3u.cn/v3u/Public/js/editor/attached/20201218161245_80896.png">
<meta property="article:published_time" content="2021-04-23T07:16:36.000Z">
<meta property="article:modified_time" content="2021-04-23T07:17:56.493Z">
<meta property="article:author" content="南辞">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://v3u.cn/v3u/Public/Uploads/1608276523.png">
  
  
    <link rel="icon" href="/tefuir/css/images/favicon.ico">
  
  
<link rel="stylesheet" href="/tefuir/css/style.css">

  

  
  <!-- baidu webmaster push -->
  <script src='//push.zhanzhang.baidu.com/push.js'></script>
<meta name="generator" content="Hexo 5.2.0"><link rel="alternate" href="/tefuir/atom.xml" title="南辞的技术博客" type="application/atom+xml">
</head>
<body class="home blog custom-background custom-font-enabled single-author">
  <div id="page" class="hfeed site">
      <header id="masthead" class="site-header" role="banner">
    <hgroup>
      <h1 class="site-title">
        <a href="/tefuir/" title="南辞的技术博客" rel="home">南辞的技术博客</a>
      </h1>
      
        <h2 class="site-description hitokoto"></h2>
        <script type="text/javascript" src="https://v1.hitokoto.cn/?encode=js"></script>
      
    </hgroup>

    <nav id="site-navigation" class="main-navigation" role="navigation">
            <button class="menu-toggle">菜单</button>
            <a class="assistive-text" href="/#content" title="跳至内容">跳至内容</a><!--TODO-->
            <div class="menu-main-container">
                <ul id="menu-main" class="nav-menu">
                
                    <li class="menu-item menu-item-type-post_type menu-item-object-page"><a href="/tefuir/">Home</a></li>
                
                    <li class="menu-item menu-item-type-post_type menu-item-object-page"><a href="/tefuir/archives">Archives</a></li>
                
                </ul>
            </div>
    </nav>
</header>

      <div id="main" class="wrapper">
        <div id="primary" class="site-content"><div id="content" role="main"><article id="post-前后端分离实现微信支付" class="post-前后端分离实现微信支付 post type-post status-publish format-standard hentry">
    <!---->

      <header class="entry-header">
        
        
  
    <h1 class="entry-title article-title">
      前后端分离实现微信支付
    </h1>
  

        
        <div class="comments-link">
            
            <a href="javascript:void(0);" data-url="https://yanlidoushikeke.gitee.io/myhexo/2021/04/23/%E5%89%8D%E5%90%8E%E7%AB%AF%E5%88%86%E7%A6%BB%E5%AE%9E%E7%8E%B0%E5%BE%AE%E4%BF%A1%E6%94%AF%E4%BB%98/" data-id="cknzsqft9000654u02hzl8jo4" class="leave-reply bdsharebuttonbox" data-cmd="more">Share</a>
        </div><!-- .comments-link -->
      </header><!-- .entry-header -->

    <div class="entry-content">
      
        <h1 id="Win10环境前后端分离项目基于Vue-js-Django-Python3实现微信-wechat-扫码支付流程-2021年最新攻略"><a href="#Win10环境前后端分离项目基于Vue-js-Django-Python3实现微信-wechat-扫码支付流程-2021年最新攻略" class="headerlink" title="Win10环境前后端分离项目基于Vue.js+Django+Python3实现微信(wechat)扫码支付流程(2021年最新攻略)"></a>Win10环境前后端分离项目基于Vue.js+Django+Python3实现微信(wechat)扫码支付流程(2021年最新攻略)</h1><p><img src="https://v3u.cn/v3u/Public/Uploads/1608276523.png" alt="Win10环境前后端分离项目基于Vue.js+Django+Python3实现微信(wechat)扫码支付流程(2021年最新攻略)"></p>
<p>在生活中真正具有广泛性、高效性、使用方便性的支付方式还得是扫码支付，扫码的优点在于推广成本低，上至钓鱼台国宾馆，下至发廊地摊都能用，打印出来就完事了，而相比其他支付方式，现金的找零及假钞问题，信用卡的办理门槛、pos机的沉没成本，就算微信可集成的h5支付和小程序支付，奈何很多老年人根本不会用小程序和手机浏览器，更别说再进行支付操作了，所以基于二维码的扫码支付的确是非常符合国情的。</p>
<p>​    本次我们使用前后端分离项目Vue.js+Django来集成微信的扫码支付功能，体验一下21世纪泛用性最高的支付方式，首先注册微信公众平台：<a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/">https://mp.weixin.qq.com</a></p>
<p>​    获得开发者id和秘钥(appid &amp; appsecret)</p>
<p><img src="https://v3u.cn/v3u/Public/js/editor/attached/20201218151220_48395.png" alt="img"></p>
<p>​    同时确保获取微信支付接口的权限：</p>
<p><img src="https://v3u.cn/v3u/Public/js/editor/attached/20201218151256_41580.png" alt="img"></p>
<p>​    随后注册微信支付商户平台：<a target="_blank" rel="noopener" href="https://pay.weixin.qq.com/">https://pay.weixin.qq.com/</a></p>
<p>​    获取微信支付的商户号(在账户信息页面)：</p>
<p><img src="https://v3u.cn/v3u/Public/js/editor/attached/20201218151258_71938.png" alt="img"></p>
<p>​    获取微信支付接口的秘钥(账户中心-&gt;api安全):</p>
<p><img src="https://v3u.cn/v3u/Public/js/editor/attached/20201218151229_51971.png" alt="img"></p>
<p>​    同时在产品中心-&gt;开发配置页面，将支付域名配置好：</p>
<p><img src="https://v3u.cn/v3u/Public/js/editor/attached/20201218151248_41646.png" alt="img"></p>
<p>​    这里不像微信小程序，小程序只能允许https协议接口，而扫码支付域名既支持https也支持http，非常方便，同时注意域名必须是一个备案域名。</p>
<p>​    至此，微信支付的前置操作就搞定了，下面我们来编写后台接口wx_pay.py，首先导入依赖的库和一些工具方法：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">import requests</span><br><span class="line">from django.http import HttpResponse, HttpResponseRedirect</span><br><span class="line"></span><br><span class="line">import random</span><br><span class="line">import time</span><br><span class="line">import hashlib</span><br><span class="line"></span><br><span class="line">import qrcode</span><br><span class="line">from bs4 import BeautifulSoup</span><br><span class="line"></span><br><span class="line">def trans_xml_to_dict(data_xml):</span><br><span class="line">    soup &#x3D; BeautifulSoup(data_xml, features&#x3D;&#39;xml&#39;)</span><br><span class="line">    xml &#x3D; soup.find(&#39;xml&#39;)  # 解析XML</span><br><span class="line">    if not xml:</span><br><span class="line">        return &#123;&#125;</span><br><span class="line">    data_dict &#x3D; dict([(item.name, item.text) for item in xml.find_all()])</span><br><span class="line">    return data_dict</span><br><span class="line"></span><br><span class="line">def trans_dict_to_xml(data_dict):  # 定义字典转XML的函数</span><br><span class="line">    data_xml &#x3D; []</span><br><span class="line">    for k in sorted(data_dict.keys()):  # 遍历字典排序后的key</span><br><span class="line">        v &#x3D; data_dict.get(k)  # 取出字典中key对应的value</span><br><span class="line">        if k &#x3D;&#x3D; &#39;detail&#39; and not v.startswith(&#39;&lt;![CDATA[&#39;):  # 添加XML标记</span><br><span class="line">            v &#x3D; &#39;&lt;![CDATA[&#123;&#125;]]&gt;&#39;.format(v)</span><br><span class="line">        data_xml.append(&#39;&lt;&#123;key&#125;&gt;&#123;value&#125;&lt;&#x2F;&#123;key&#125;&gt;&#39;.format(key&#x3D;k, value&#x3D;v))</span><br><span class="line">    return &#39;&lt;xml&gt;&#123;&#125;&lt;&#x2F;xml&gt;&#39;.format(&#39;&#39;.join(data_xml))  # 返回XML</span><br><span class="line"></span><br><span class="line">def get_sign(data_dict, key):  # 签名函数，参数为签名的数据和密钥</span><br><span class="line">    params_list &#x3D; sorted(data_dict.items(), key&#x3D;lambda e: e[0], reverse&#x3D;False)  # 参数字典倒排序为列表</span><br><span class="line">    params_str &#x3D; &quot;&amp;&quot;.join(u&quot;&#123;&#125;&#x3D;&#123;&#125;&quot;.format(k, v) for k, v in params_list) + &#39;&amp;key&#x3D;&#39; + key</span><br><span class="line">    # 组织参数字符串并在末尾添加商户交易密钥</span><br><span class="line">    md5 &#x3D; hashlib.md5()  # 使用MD5加密模式</span><br><span class="line">    md5.update(params_str.encode())  # 将参数字符串传入</span><br><span class="line">    sign &#x3D; md5.hexdigest().upper()  # 完成加密并转为大写</span><br><span class="line">    return sign</span><br></pre></td></tr></table></figure>

<p>​    qrcode模块用来生成二维码，bs4模块用来将微信接口返回的xml解析成json，在21世纪的第二十个年头，微信接口居然还在使用原始的xml，这种反人类行为实在不能理解。</p>
<p>​    接下来我们来编写支付逻辑，参考微信官方文档：<a target="_blank" rel="noopener" href="https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5&amp;index=3">https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5&amp;index=3</a></p>
<blockquote>
<p>业务流程说明：<br>（1）商户后台系统根据用户选购的商品生成订单。<br>（2）用户确认支付后调用微信支付【统一下单API】生成预支付交易；<br>（3）微信支付系统收到请求后生成预支付交易单，并返回交易会话的二维码链接code_url。<br>（4）商户后台系统根据返回的code_url生成二维码。<br>（5）用户打开微信“扫一扫”扫描二维码，微信客户端将扫码内容发送到微信支付系统。<br>（6）微信支付系统收到客户端请求，验证链接有效性后发起用户支付，要求用户授权。<br>（7）用户在微信客户端输入密码，确认支付后，微信客户端提交授权。<br>（8）微信支付系统根据用户授权完成支付交易。<br>（9）微信支付系统完成支付交易后给微信客户端返回交易结果，并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。<br>（10）微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况，通知微信后台系统不再发送该单的支付通知。<br>（11）未收到支付通知的情况，商户后台系统调用【查询订单API】。<br>（12）商户确认订单已支付后给用户发货。</p>
</blockquote>
<p>​    一望而知，我们需要调用微信的统一下单接口，文档：<a target="_blank" rel="noopener" href="https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1">https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1</a></p>
<p>​    编写逻辑：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">def wx_pay(request):</span><br><span class="line"></span><br><span class="line">    url &#x3D; &#39;https:&#x2F;&#x2F;api.mch.weixin.qq.com&#x2F;pay&#x2F;unifiedorder&#39;  # 微信扫码支付接口</span><br><span class="line">    key &#x3D; &#39;945bec********a8fbf7d7&#39;  #商户api秘钥</span><br><span class="line">    total_fee &#x3D; 1 #支付金额，单位分</span><br><span class="line">    body &#x3D; &#39;123123&#39;  # 商品描述</span><br><span class="line">    out_trade_no &#x3D; &#39;order_%s&#39; % random.randrange(100000, 999999)  # 订单编号</span><br><span class="line">    params &#x3D; &#123;</span><br><span class="line">        &#39;appid&#39;: &#39;wx09*****f&#39;,  # APPID</span><br><span class="line">        &#39;mch_id&#39;: &#39;16****08&#39;,  # 商户号</span><br><span class="line">        &#39;notify_url&#39;: &#39;http:&#x2F;&#x2F;wxpay.v3u.cn&#x2F;wx_back&#x2F;&#39;,  # 支付域名回调地址</span><br><span class="line">        &#39;product_id&#39;: &#39;goods_%s&#39; % random.randrange(100000, 999999),  # 商品编号</span><br><span class="line">        &#39;trade_type&#39;: &#39;NATIVE&#39;,  # 支付类型（扫码支付）</span><br><span class="line">        &#39;spbill_create_ip&#39;: &#39;114.254.176.137&#39;,  # 发送请求服务器的IP地址</span><br><span class="line">        &#39;total_fee&#39;: total_fee,  # 订单总金额</span><br><span class="line">        &#39;out_trade_no&#39;: out_trade_no,  # 订单编号</span><br><span class="line">        &#39;body&#39;: body,  # 商品描述</span><br><span class="line">        &#39;nonce_str&#39;: &#39;ibuaiVcKdpRxkhJA&#39;  # 字符串</span><br><span class="line">    &#125;</span><br><span class="line">    sign &#x3D; get_sign(params, key)  # 获取签名</span><br><span class="line">    params.setdefault(&#39;sign&#39;, sign)  # 添加签名到参数字典</span><br><span class="line">    xml &#x3D; trans_dict_to_xml(params)  # 转换字典为XML</span><br><span class="line">    response &#x3D; requests.request(&#39;post&#39;, url, data&#x3D;xml)  # 以POST方式向微信公众平台服务器发起请求</span><br><span class="line">    data_dict &#x3D; trans_xml_to_dict(response.content)  # 将请求返回的数据转为字典</span><br><span class="line">    print(data_dict)</span><br><span class="line">    qrcode_name &#x3D; out_trade_no + &#39;.png&#39;  # 支付二维码图片保存路径</span><br><span class="line">    if data_dict.get(&#39;return_code&#39;) &#x3D;&#x3D; &#39;SUCCESS&#39;:  # 如果请求成功</span><br><span class="line">        img &#x3D; qrcode.make(data_dict.get(&#39;code_url&#39;))  # 创建支付二维码片</span><br><span class="line">        img.save(&#39;.&#x2F;&#39; + qrcode_name)  # 保存支付二维码</span><br><span class="line">    return HttpResponse(qrcode_name)</span><br></pre></td></tr></table></figure>

<p>​    随后配置路由:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">from myapp.wx_pay import wx_pay</span><br><span class="line">from django.contrib.staticfiles.urls import staticfiles_urlpatterns</span><br><span class="line"></span><br><span class="line"># ... the rest of your URLconf goes here ...</span><br><span class="line">urlpatterns &#x3D; [</span><br><span class="line">    #定义超链接路由</span><br><span class="line">    re_path(&#39;^static&#x2F;upload&#x2F;(?P&lt;path&gt;.*)$&#39;,serve,&#123;&#39;document_root&#39;:&#39;&#x2F;static&#x2F;upload&#x2F;&#39;&#125;),</span><br><span class="line">    path(&#39;wx_pay&#x2F;&#39;, wx_pay),</span><br><span class="line">]</span><br></pre></td></tr></table></figure>

<p>​    启动django服务：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python manage.py runserver</span><br></pre></td></tr></table></figure>

<p>​    访问<a target="_blank" rel="noopener" href="http://localhost:8000/wx_pay/">http://localhost:8000/wx_pay/</a></p>
<p><img src="https://v3u.cn/v3u/Public/js/editor/attached/20201218161202_30556.png" alt="img"></p>
<p>​    没有问题，查看后台日志：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;&#39;return_code&#39;: &#39;SUCCESS&#39;, &#39;return_msg&#39;: &#39;OK&#39;, &#39;appid&#39;: &#39;wx092344a76b9979ff&#39;, &#39;mch_id&#39;: &#39;1602932608&#39;, &#39;nonce_str&#39;: &#39;bnJwGlXZ3eDSNgjs&#39;, &#39;sign&#39;: &#39;2D81402DABEDF75E9A58F200FE7B6775&#39;, &#39;result_code&#39;: &#39;SUCCESS&#39;, &#39;prepay_id&#39;: &#39;wx1816114416896958d6f84177bd71da0000&#39;, &#39;trade_type&#39;: &#39;NATIVE&#39;, &#39;code_url&#39;: &#39;weixin:&#x2F;&#x2F;wxpay&#x2F;bizpayurl?pr&#x3D;JgBYgTS00&#39;&#125;</span><br></pre></td></tr></table></figure>

<p>​    可以看到已经下单成功，不过订单状态处于预支付状态，同时检查二维码图片是否生成：</p>
<p><img src="https://v3u.cn/v3u/Public/js/editor/attached/20201218161240_52822.png" alt="img"></p>
<p>​    至此，后台逻辑基本搞定，下面就是如何在前端进行调用，同时让用户进行扫描操作，编写wx_pay.vue组件：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line"></span><br><span class="line">  &lt;div&gt;</span><br><span class="line"></span><br><span class="line">    &lt;center&gt;&lt;h1&gt;扫码支付&lt;&#x2F;h1&gt;&lt;&#x2F;center&gt;</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line">    &lt;a-form-item v-bind&#x3D;&quot;formItemLayout&quot; label&#x3D;&quot;金额&quot;&gt;</span><br><span class="line">      &lt;a-input v-model&#x3D;&quot;money&quot;&#x2F;&gt;</span><br><span class="line">    &lt;&#x2F;a-form-item&gt;</span><br><span class="line">    </span><br><span class="line">   </span><br><span class="line">    &lt;a-form-item v-bind&#x3D;&quot;tailFormItemLayout&quot;&gt;</span><br><span class="line">      &lt;a-button type&#x3D;&quot;primary&quot; html-type&#x3D;&quot;submit&quot; @click&#x3D;&quot;submit&quot;&gt;</span><br><span class="line">        生成二维码</span><br><span class="line">      &lt;&#x2F;a-button&gt;</span><br><span class="line">    &lt;&#x2F;a-form-item&gt;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    &lt;a-form-item v-bind&#x3D;&quot;formItemLayout&quot; label&#x3D;&quot;二维码&quot;&gt;</span><br><span class="line">    </span><br><span class="line">      &lt;img :src&#x3D;&quot;src&quot; &#x2F;&gt;</span><br><span class="line"></span><br><span class="line">    &lt;&#x2F;a-form-item&gt;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">   &lt;&#x2F;div&gt;</span><br><span class="line"></span><br><span class="line">&lt;&#x2F;template&gt;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&lt;script&gt;</span><br><span class="line"></span><br><span class="line">export default &#123;</span><br><span class="line">  data() &#123;</span><br><span class="line">    return &#123;</span><br><span class="line">      money:&quot;1&quot;,</span><br><span class="line">      src:&quot;&quot;,</span><br><span class="line">      formItemLayout: &#123;</span><br><span class="line">        labelCol: &#123;</span><br><span class="line">          xs: &#123; span: 24 &#125;,</span><br><span class="line">          sm: &#123; span: 8 &#125;,</span><br><span class="line">        &#125;,</span><br><span class="line">        wrapperCol: &#123;</span><br><span class="line">          xs: &#123; span: 24 &#125;,</span><br><span class="line">          sm: &#123; span: 16 &#125;,</span><br><span class="line">        &#125;,</span><br><span class="line">      &#125;,</span><br><span class="line">      tailFormItemLayout: &#123;</span><br><span class="line">        wrapperCol: &#123;</span><br><span class="line">          xs: &#123;</span><br><span class="line">            span: 24,</span><br><span class="line">            offset: 0,</span><br><span class="line">          &#125;,</span><br><span class="line">          sm: &#123;</span><br><span class="line">            span: 16,</span><br><span class="line">            offset: 8,</span><br><span class="line">          &#125;,</span><br><span class="line">        &#125;,</span><br><span class="line">      &#125;,</span><br><span class="line">      dataSource: [</span><br><span class="line">        &#123;</span><br><span class="line">          key: &#39;0&#39;,</span><br><span class="line">          name: &#39;Edward King 0&#39;,</span><br><span class="line">          age: &#39;32&#39;,</span><br><span class="line">          address: &#39;London, Park Lane no. 0&#39;,</span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">          key: &#39;1&#39;,</span><br><span class="line">          name: &#39;Edward King 1&#39;,</span><br><span class="line">          age: &#39;32&#39;,</span><br><span class="line">          address: &#39;London, Park Lane no. 1&#39;,</span><br><span class="line">        &#125;,</span><br><span class="line">      ],</span><br><span class="line">      columns: [</span><br><span class="line">        &#123;</span><br><span class="line">          title: &#39;name&#39;,</span><br><span class="line">          dataIndex: &#39;name&#39;,</span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">          title: &#39;age&#39;,</span><br><span class="line">          dataIndex: &#39;age&#39;,</span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">          title: &#39;address&#39;,</span><br><span class="line">          dataIndex: &#39;address&#39;,</span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">          title: &#39;operation&#39;,</span><br><span class="line">          dataIndex: &#39;operation&#39;,</span><br><span class="line">          scopedSlots: &#123; customRender: &#39;operation&#39; &#125;,</span><br><span class="line">        &#125;,</span><br><span class="line">      ],</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;,</span><br><span class="line">  methods: &#123;</span><br><span class="line"></span><br><span class="line">    submit:function()&#123;</span><br><span class="line"></span><br><span class="line">       this.axios.get(&#39;http:&#x2F;&#x2F;localhost:8000&#x2F;wx_pay&#x2F;&#39;).then((result) &#x3D;&gt;&#123;</span><br><span class="line">    </span><br><span class="line">            console.log(result.data.img);</span><br><span class="line"></span><br><span class="line">            this.src &#x3D; &quot;http:&#x2F;&#x2F;localhost:8000&#x2F;static&#x2F;upload&#x2F;&quot;+result.data.img</span><br><span class="line">  </span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">    &#125;,</span><br><span class="line">    onDelete(key) &#123;</span><br><span class="line">      console.log(this.dataSource[key]);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br><span class="line">&lt;&#x2F;script&gt;</span><br></pre></td></tr></table></figure>

<p>​    当用户点击按钮之后，旋即请求后端支付接口，将接口生成的二维码返回给前端，效果是这样的：</p>
<p><img src="https://v3u.cn/v3u/Public/js/editor/attached/20201218161242_76881.png" alt="img"></p>
<p>​    随后使用微信扫一扫功能进行扫码支付，需要注意的是，该二维码有效期只有五分钟，所以最好加上刷新功能。</p>
<p>​    <img src="https://v3u.cn/v3u/Public/js/editor/attached/20201218161259_75018.jpeg" alt="img"></p>
<p>​    支付成功之后，我们还需要对交易进行确认，所以根据微信官方文档，调用统一查询接口：</p>
<p><a target="_blank" rel="noopener" href="https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_2%EF%BC%8C%E6%A0%B9%E6%8D%AE%E6%8E%A5%E5%8F%A3%E6%96%87%E6%A1%A3%E7%BC%96%E5%86%99%E9%80%BB%E8%BE%91%EF%BC%9A">https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_2，根据接口文档编写逻辑：</a></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">def wx_check(request):</span><br><span class="line"></span><br><span class="line">    #统一订单查询接口</span><br><span class="line"></span><br><span class="line">    url &#x3D; &quot;https:&#x2F;&#x2F;api.mch.weixin.qq.com&#x2F;pay&#x2F;orderquery&quot;</span><br><span class="line"></span><br><span class="line">    out_trade_no &#x3D; &quot;order_537236&quot; #支付后的商户订单号</span><br><span class="line">    key &#x3D; &#39;945b******d7&#39;  # 商户api密钥</span><br><span class="line"></span><br><span class="line">    params &#x3D; &#123;</span><br><span class="line">        &#39;appid&#39;: &#39;wx0*****ff&#39;,  # APPID</span><br><span class="line">        &#39;mch_id&#39;: &#39;16*****08&#39;,  # 商户号</span><br><span class="line">        &#39;out_trade_no&#39;: out_trade_no,  # 订单编号</span><br><span class="line">        &#39;nonce_str&#39;: &#39;ibuaiVcKdpRxkhJA&#39;  # 随机字符串</span><br><span class="line">    &#125;</span><br><span class="line">    sign &#x3D; get_sign(params, key)  # 获取签名</span><br><span class="line">    params.setdefault(&#39;sign&#39;, sign)  # 添加签名到参数字典</span><br><span class="line">    xml &#x3D; trans_dict_to_xml(params)  # 转换字典为XML</span><br><span class="line">    response &#x3D; requests.request(&#39;post&#39;, url, data&#x3D;xml)  # 以POST方式向微信公众平台服务器发起请求</span><br><span class="line">    data_dict &#x3D; trans_xml_to_dict(response.content)  # 将请求返回的数据转为字典</span><br><span class="line">    print(data_dict)</span><br><span class="line"></span><br><span class="line">    return HttpResponse(&#39;ok&#39;)</span><br></pre></td></tr></table></figure>

<p>​    这里需要注意的是，查询的订单编号可以使商户自己的订单编号，也可以是微信订单号，二者必取其一：</p>
<p><img src="https://v3u.cn/v3u/Public/js/editor/attached/20201218161259_98962.png" alt="img"></p>
<p>​    访问接口 <a target="_blank" rel="noopener" href="http://localhost:8000/wx_check/">http://localhost:8000/wx_check/</a></p>
<p>​    返回结果：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;&#39;return_code&#39;: &#39;SUCCESS&#39;, &#39;return_msg&#39;: &#39;OK&#39;, &#39;appid&#39;: &#39;wx092344a76b9979ff&#39;, &#39;mch_id&#39;: &#39;1602932608&#39;, &#39;nonce_str&#39;: &#39;BVoaDmxxADkpSFEl&#39;, &#39;sign&#39;: &#39;23A86EB406B743E0C2C61C7E78DC9373&#39;, &#39;result_code&#39;: &#39;SUCCESS&#39;, &#39;openid&#39;: &#39;oy9q36f9Dpeokj9FWyN3j0znpIqE&#39;, &#39;is_subscribe&#39;: &#39;N&#39;, &#39;trade_type&#39;: &#39;NATIVE&#39;, &#39;bank_type&#39;: &#39;OTHERS&#39;, &#39;total_fee&#39;: &#39;1&#39;, &#39;fee_type&#39;: &#39;CNY&#39;, &#39;transaction_id&#39;: &#39;4200000806202012174121934231&#39;, &#39;out_trade_no&#39;: &#39;order_537236&#39;, &#39;attach&#39;: &#39; &#39;, &#39;time_end&#39;: &#39;20201217231553&#39;, &#39;trade_state&#39;: &#39;SUCCESS&#39;, &#39;cash_fee&#39;: &#39;1&#39;, &#39;trade_state_desc&#39;: &#39;支付成功&#39;, &#39;cash_fee_type&#39;: &#39;CNY&#39;&#125;</span><br></pre></td></tr></table></figure>

<p>​    可以看到没有问题，但是由于涉及金钱业务，为了养成良好的测试习惯，最好登录商户后台再次确认：</p>
<p><img src="https://v3u.cn/v3u/Public/js/editor/attached/20201218161245_80896.png" alt="img"></p>
<p>​    结语：至此，整个微信扫码支付流程全部跑通，流程上比微信小程序支付逻辑要简单一些，同时由于不需要在线用户的openid，所以像微信小程序获取不到openid这样的大坑并不存在，后续会分享一些关于微信扫码订单退款的逻辑，搞笑的是，统一下单和查询接口没有并发限制，而申请退款居然有qps上的限制，所以退款流程应该会需要消息队列的介入。</p>

      
    </div><!-- .entry-content -->

    <footer class="entry-meta">
    <a href="/tefuir/2021/04/23/%E5%89%8D%E5%90%8E%E7%AB%AF%E5%88%86%E7%A6%BB%E5%AE%9E%E7%8E%B0%E5%BE%AE%E4%BF%A1%E6%94%AF%E4%BB%98/">
    <time datetime="2021-04-23T07:16:36.000Z" class="entry-date">
        2021-04-23
    </time>
</a>
    
    
    </footer>
</article>


    
<nav class="nav-single">
    <h3 class="assistive-text">文章导航</h3>
    
        <span class="nav-previous"><a href="/tefuir/2021/04/23/%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%97%AE%E9%A2%98/" rel="prev"><span class="meta-nav">←</span> 服务器问题</a></span>
    
    
        <span class="nav-next"><a href="/tefuir/2021/04/21/%E5%BC%80%E5%8F%91%E4%B8%AD%E7%9A%84%E5%9B%B0%E9%9A%BE/" rel="next">开发中的困难 <span class="meta-nav">→</span></a></span>
    
</nav><!-- .nav-single -->







</div></div>
        <div id="secondary" class="widget-area" role="complementary">
  
    <aside id="search" class="widget widget_search"><form role="search" method="get" accept-charset="utf-8" id="searchform" class="searchform" action="//google.com/search">
    <div>
        <input type="text" value="" name="s" id="s" />
        <input type="submit" id="searchsubmit" value="搜索" />
    </div>
</form></aside>
  
    
  
    
  
    
  <aside class="widget">
    <h3 class="widget-title">Recents</h3>
    <div class="widget-content">
      <ul>
        
          <li>
            <a href="/tefuir/2021/04/26/python%E5%9F%BA%E7%A1%80%E7%90%86%E8%AE%BA%E5%AE%9D%E5%85%B8/">python基础理论宝典</a>
          </li>
        
          <li>
            <a href="/tefuir/2021/04/23/python3-Flask%E7%BB%93%E5%90%88Socket-io%E7%AE%80%E5%8D%95%E5%AE%9E%E7%8E%B0%E5%9C%A8%E7%BA%BF%E5%AE%A2%E6%9C%8D%E7%B3%BB%E7%BB%9F/">python3+Flask结合Socket.io简单实现在线客服系统</a>
          </li>
        
          <li>
            <a href="/tefuir/2021/04/23/win10%E7%B3%BB%E7%BB%9F%E4%B8%8B%E5%88%A9%E7%94%A8docker%E9%83%A8%E7%BD%B2gunicorn-Flask%E6%89%93%E9%80%A0%E7%8B%AC%E7%AB%8B%E9%95%9C%E5%83%8F/">win10系统下利用docker部署gunicorn+Flask打造独立镜像</a>
          </li>
        
          <li>
            <a href="/tefuir/2021/04/23/win10%E7%B3%BB%E7%BB%9F%E4%B8%8B%E5%9F%BA%E4%BA%8Edocker%E9%85%8D%E7%BD%AEelasticsearch%E9%85%8D%E5%90%88python3%E8%BF%9B%E8%A1%8C%E5%85%A8%E6%96%87%E6%A3%80%E7%B4%A2/">win10系统下基于docker配置elasticsearch配合python3进行全文检索</a>
          </li>
        
          <li>
            <a href="/tefuir/2021/04/23/%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%97%AE%E9%A2%98/">服务器问题</a>
          </li>
        
      </ul>
    </div>
  </aside>

  
    
  
    
  
</div>
      </div>
      <footer id="colophon" role="contentinfo">
    <p>&copy; 2021 南辞
    All rights reserved.</p>
    <p>Powered by <a href="http://hexo.io/" target="_blank">Hexo</a></p>
</footer>
    <script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"1","bdMiniList":false,"bdPic":"","bdStyle":"2","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='/js/share.js'];</script>

<script src="/js/jquery-3.3.1.min.js"></script>


  
<link rel="stylesheet" href="/tefuir/fancybox/jquery.fancybox.css">

  
<script src="/tefuir/fancybox/jquery.fancybox.pack.js"></script>




<script src="/tefuir/js/script.js"></script>


<script src="/js/navigation.js"></script>

<div id="bg"></div>

  </div>
</body>
</html>